package com.powernode.service.impl;

import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.google.common.collect.Lists;
import com.powernode.constant.RedisConstant;
import com.powernode.entity.Category;
import com.powernode.entity.Product;
import com.powernode.exception.BizException;
import com.powernode.mapper.CategoryMapper;
import com.powernode.mapper.ProductMapper;
import com.powernode.service.CategoryService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.service.ProductService;
import com.powernode.util.MinioUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Predicate;

/**
 * <p>
 * 商品分类 服务实现类
 * </p>
 *
 * @author Juice
 * @since 2023-01-11
 */
@Service
//缓存使用类
@CacheConfig(cacheNames = RedisConstant.KEY_INDEX_CATEGORY)
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {

    @Autowired
    private MinioUtil minioUtil;
    /**
    * 商品分类列表转为树形列表
    */
    @Override
    public List<Tree<Long>> tree() {
        //根据当前业务层的list()方法获取商品分类列表
        List<Category> list = this.list();
        //new一个树节点集合，TreeNode就是树形列表中的每个节点
        List<TreeNode<Long>> treeNodeList = Lists.newArrayList();
        for (Category category : list) {
            //每个树节点中放入商品分类的信息，泛型是父类id的数据类型（也是将普通列表转为树形列表的核心属性）
            TreeNode<Long> treeNode = new TreeNode<>(category.getId(), category.getParentId(), category.getName(), category.getSort());

            Map<String,Object> extra = new HashMap<>();
            extra.put("icon", category.getIcon());
            //在new TreeNode(category.getSort())时，hutool工具包会设置为"weight":category.getSort()
            //而数据库中该字段名为sort，为了前端与后台一致性，所以手动添加一个额外属性，map("sort",category.getSort())
            extra.put("sort", category.getSort());
            extra.put("level", category.getLevel());
            //树节点的setExtra()方法是将每个节点对象的扩展属性或者不重要的属性封装起来放在一个map集合对象中
            treeNode.setExtra(extra);
            //将每个树节点放入树形列表中
            treeNodeList.add(treeNode);
        }
        //使用hutool工具类中TreeUtil的build()方法创建一个树形列表，第一个参数为Tree<>即一个树形集合，第二个参数为父id即树形列表根节点的id值
        return TreeUtil.build(treeNodeList,0L);
    }

    @Override
    public Category getCategoryById(Long categoryId) {
        return this.getById(categoryId);
    }

    /**
     * 新增一个商品分类
     */
    @Override
    @CacheEvict(allEntries = true) //增删改，清除所有缓存，因为查询的缓存是存储的一个集合，更新时不能精确定位到个体
    public Category saveCategory(Category category) {
        this.save(category);
        return category;
    }

    /**
    * 扩展一个商品分类对象编辑的方法，如果编辑失败抛出自定义异常
    */
    @Override
    @CacheEvict(allEntries = true) //增删改，清除所有缓存，因为查询的缓存是存储的一个集合，更新时不能精确定位到个体
    public Category updateCategoryById(Category category) {
        //如果用户更改了父分类或分类层级，则不允许修改
        Category originCat = this.getById(category.getId());
        if (originCat.getParentId() != category.getParentId() || originCat.getLevel() != category.getLevel()){
            throw new BizException(HttpStatus.BAD_REQUEST.value(), "商品分类的父分类或分类层级不允许修改");
        }
        //更新
        boolean result = this.updateById(category);
        if (!result) {
            throw new BizException(HttpStatus.INTERNAL_SERVER_ERROR.value(), "商品分类id不存在");
        }

        return category;

    }

    @Autowired
    private ProductMapper productMapper;
    /**
    * 扩展一个根据商品分类id单删
    */
    @Override
    @CacheEvict(allEntries = true) //增删改，清除所有缓存，因为查询的缓存是存储的一个集合，更新时不能精确定位到个体
    public boolean removeCategoryById(Long categoryId) {
        Category category = this.getCategoryById(categoryId);
        if (category == null) {
            throw new BizException(HttpStatus.BAD_REQUEST.value(), "该分类不存在");
        }
        //判断该分类下是否存在子分类
        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Category::getParentId,categoryId);
        //mybatis-plus的持久层有一个方法exists(queryWrapper)可以根据条件构造器判断查询的数据是否存在
        if (baseMapper.exists(queryWrapper)) {
            throw new BizException(HttpStatus.BAD_REQUEST.value(), "该分类下存在子分类，不能删除");
        }
        //判断该分类下是否存在商品
        LambdaQueryWrapper<Product> queryWrapper1 = new LambdaQueryWrapper<>();
        queryWrapper1.eq(Product::getCategoryId,categoryId);
        if (productMapper.exists(queryWrapper1)) {
            throw new BizException(HttpStatus.BAD_REQUEST.value(), "该分类下存在商品，不能删除");
        }

        //删除商品分类信息
        boolean result = this.removeById(categoryId);
        //删除商品分类图标
        String icon = category.getIcon();
        if (StrUtil.isNotBlank(icon)){
            minioUtil.removeFile(icon);
        }
        return result;
    }

    @Override
    /**
     * 使用redis缓存将返回结果缓存到redis中
     */
    @Cacheable
    public List<Category> listCategoriesByLevel(Integer level) {

        LambdaQueryWrapper<Category> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Category::getLevel,level);
        wrapper.orderByAsc(Category::getSort);
        return this.list(wrapper);
    }
}
